# SEE modeldata package for new datasets
library(tidyverse)         # for graphing and data cleaning
library(tidymodels)        # for modeling
library(ranger)            # for random forest - will need for shiny app
library(lubridate)         # for date manipulation
library(themis)            # for up and downsampling
library(DALEX)             # for model interpretation  
library(DALEXtra)          # for extension of DALEX
library(lime)
theme_set(theme_minimal()) # Lisa's favorite theme
data("lending_club")
# Data dictionary (as close as I could find): https://www.kaggle.com/wordsforthewise/lending-club/discussion/170691

Put it on GitHub! – DONE

Github Repo Link: https://github.com/apalma127/assignment-5

Interpretable ML methods – DONE

We will once again use the lending club data that we used in the 3rd assignment. We will focus on the random forest model, which I recreate below. (Note we use this model even though the true negative rate of the training set is quite bad.)

set.seed(494) # for reproducibility

#split data
lending_split <- initial_split(lending_club,
                               prop = .75,
                               strata = Class)

lending_training <- training(lending_split)
lending_test <- testing(lending_split)


#create recipe - including up and downsampling for model fitting
set.seed(456)
rf_recipe <- 
  recipe(Class ~ .,
         data = lending_training) %>% 
  step_upsample(Class, over_ratio = .5) %>% 
  step_downsample(Class, under_ratio = 1) %>% 
  step_mutate_at(all_numeric(), 
                 fn = ~as.numeric(.))

# create model
rf_model <- 
  rand_forest(mtry = tune(), 
              min_n = tune(), 
              trees = 100) %>% 
  set_mode("classification") %>% 
  set_engine("ranger")

# create workflow
rf_workflow <-
  workflow() %>% 
  add_recipe(rf_recipe) %>% 
  add_model(rf_model)

  grid_regular(finalize(mtry(),
                        lending_training %>%
                          select(-Class)),
               min_n(),
               levels = 3)
# create penalty grid
  rf_penalty_grid <- 
grid_regular(finalize(mtry(),
                        lending_training %>%
                          select(-Class)),
               min_n(),
               levels = 3)


# create cv samples
set.seed(494) #for reproducible 5-fold
lending_cv <- vfold_cv(lending_training,
                       v = 5)

# tune model
rf_tune <- 
  rf_workflow %>% 
  tune_grid(
    resamples = lending_cv,
    grid = rf_penalty_grid
  )

# find model with best accuracy
best_accuracy <-
  rf_tune %>% 
  select_best(metric = "accuracy")

# finalize model
rf_final <- rf_workflow %>% 
  finalize_workflow(best_accuracy) %>% 
  fit(data = lending_training)

1. Use functions from the DALEX and DALEXtra libraries to create a histogram and boxplot of the residuals from the training data.

rf_explain <- 
  explain_tidymodels(
    model = rf_final,
    data = lending_training %>% select(-Class), 
    y = lending_training %>% 
      mutate(Class_num = as.integer(Class =="good")) %>% 
      pull(Class_num),
    label = "rf"
  )
## Preparation of a new explainer is initiated
##   -> model label       :  rf 
##   -> data              :  7392  rows  22  cols 
##   -> data              :  tibble converted into a data.frame 
##   -> target variable   :  7392  values 
##   -> predict function  :  yhat.workflow  will be used (  default  )
##   -> predicted values  :  No value for predict function target column. (  default  )
##   -> model_info        :  package tidymodels , ver. 0.1.3 , task classification (  default  ) 
##   -> predicted values  :  numerical, min =  0 , mean =  0.8477854 , max =  1  
##   -> residual function :  difference between y and yhat (  default  )
##   -> residuals         :  numerical, min =  -0.095 , mean =  0.1004018 , max =  0.72  
##   A new explainer has been created! 
rf_mod_perf <-  model_performance(rf_explain)

rf_mod_perf
## Measures for:  classification
## recall     : 0.9897275 
## precision  : 1 
## f1         : 0.9948372 
## accuracy   : 0.9902597 
## auc        : 1
## 
## Residuals:
##     0%    10%    20%    30%    40%    50%    60%    70%    80%    90%   100% 
## -0.095  0.000  0.010  0.020  0.040  0.060  0.080  0.120  0.180  0.270  0.720
hist_plot <- 
  plot(rf_mod_perf,
       geom = "histogram")


box_plot <-
  plot(rf_mod_perf,
       geom = "boxplot")
hist_plot

box_plot

How do they look? Any interesting behavior?

They are not centered around 0 as you would want a good model to do. They appear to skew right heavily well into the positives indicating a continuous underprediction. This is very significant as it shows a pattern of underpredictions which shouldn’t be a constant pattern if it is a good model.

2. Use DALEX functions to create a variable importance plot from this model.

rf_var_imp <- 
  model_parts(
    rf_explain
    )

plot(rf_var_imp, show_boxplots = TRUE)

What are the most important variables?

The most importannt variables used to help correctly predict if a loan was paid back was interest rate, sub_grade, open_il_24m, and annual income. These are not surprising at all because most of them relate exactly back to affecting one’s ability to repay … ie if an interest rate is incredibly high it makes the pay back extremely less likely, if one has a very low income it makes payback very difficult….

3. Write a function called cp_profile to make a CP profile.

The function will take an explainer, a new observation, and a variable name as its arguments and

create a CP profile for a quantitative predictor variable.

You will need to use the predict_profile() function inside the function you create - put the variable name there so the plotting part is easier.

obs2 <- lending_training %>% 
  slice(2)
obs2

CODE TEST FOR ANNUAL INC AND INT RATE

is.integer(lending_training$annual_inc)
## [1] FALSE
is.integer(lending_training$int_rate)
## [1] FALSE

TEST 1

cp_profile_test <- predict_profile(explainer = rf_explain, 
                          new_observation = obs2,
                          variables = c("annual_inc", "int_rate"))


cp_profile_test
cp_profile_test %>% 
  filter(`_vname_` %in% c("annual_inc")) %>% 
  ggplot(aes(x = annual_inc,
             y = `_yhat_`)) +
  geom_line() 

With y_hat being our predicted class outcome (with 1 = good pay back on time and 0 = bad not paid back on time) it appears to show us: as income increases, while it doesn’t appear to be a major difference, it is slightly more likely the person is not on time w repayment. Yet, it appears it is much more constant w the trend line for higher income whereas lower income is much more chaotic in predicted payback.

TEST 2

cp_profile_test %>% 
  filter(`_vname_` %in% c("int_rate")) %>% 
  ggplot(aes(x = int_rate,
             y = `_yhat_`)) +
  geom_line() 

With y_hat being our predicted class outcome (with 1 = good pay back on time and 0 = bad not paid back on time) it appears to show us a very obvious trend. As expected, as the interest rate increases meaning increasingly more money is owed back than borrowed, the ability to pay on time and the predicted class value races downward closer to bad meaning the payment is much less likely to be on time.

Now Function Writing time…

Write a function called cp_profile to make a CP profile.

The function will take an explainer, a new observation, and a variable name as its arguments and

create a CP profile for a quantitative predictor variable.

You will need to use the predict_profile() function inside the function you create - put the variable name there so the plotting part is easier.

You’ll also want to use .data[[]] rather than aes() and quote the variables. Use the cp_profile() function to create one CP profile of your choosing.

Be sure to choose a variable that is numeric, not integer. There seem to be issues with those that I’m looking into.

 cp_profile <- function(explainer_fn, new_obs, `var`) {
  
  
  profile <- predict_profile(explainer = explainer_fn, 
                          new_observation = new_obs,
                          variables = `var`)
  
  graph <- profile %>% 
    filter(`_vname_` %in% c(`var`)) %>% 
    ggplot(aes(x = .data[[`var`]], y = `_yhat_`)) +
    geom_line() 

  
  graph
 }
lending_numeric <- select_if(lending_training, is.numeric)            

lending_numeric
cp_profile(rf_explain, obs2, "annual_inc")

cp_profile(rf_explain, obs2, "int_rate")

cp_profile(rf_explain, obs2, "funded_amnt")

cp_profile(rf_explain, obs2, "revol_util")

4. Use DALEX functions to create partial dependence plots (with the CP profiles in gray) for the 3-4 most important variables.

If the important variables are categorical, you can instead make a CP profile for 3 observations in the dataset and discuss how you could go about constructing a partial dependence plot for a categorical variable (you don’t have to code it, but you can if you want an extra challenge).

Most Important: interest rate, sub_grade, open_il_24m, and annual income

is.factor(lending_training$int_rate)
## [1] FALSE
is.factor(lending_training$sub_grade)
## [1] TRUE
is.factor(lending_training$open_il_24m)
## [1] FALSE
is.factor(lending_training$annual_inc)
## [1] FALSE
rf_pdp <- model_profile(explainer = rf_explain, 
                        variables = c("int_rate", "open_il_24m", "annual_inc"))

plot(rf_pdp, 
     variables = c("int_rate", "open_il_24m", "annual_inc"),
     geom = "profiles")

Discuss how you could go about constructing a partial dependence plot for a categorical variable

– Subgrade = Categorical

– Steps to Create PDP for subgrade

++++ one major step to do and then can use code above: convert from categorical / factor to numeric value !!!

– for each level of subgrade, working from top to bottom, assign a numeric value using a series of ifelse statements within mutate for subgrade within lending training

– now, w subgrade being numeric, can feed right into the list of variables above…

5. Choose 3 observations and do the following for each observation:

OBSERVATION 2

obs2_q5 <- lending_test %>% 
  slice(2)
obs2_q5
  • Construct a break-down plot using the default ordering.
pp_rf_2 <- predict_parts(explainer = rf_explain,
                          new_observation = obs2_q5,
                          type = "break_down") 

plot(pp_rf_2)

Interpret the resulting graph. Which variables contribute most to each observation’s prediction?

First we can seethe initial bars start at the intercept of 0.848 which marks the value of the average predicted class (1 = good, 0 = bad) when applying the rf model to the training data.

We can see we end up 0.108 lower in our predicted class of paying back a loan than the intercept thanks to some heavy hitting negative variables. The resulting 0.74 is a fairly strong good prediction.

We can see that the largest and most influential tugs from variables down towards the actual predicted value from the intercept comes from:

total_bal_il == 55445 –> -0.048

num_il_tl == 10 –> -0.032

open_il_24m == 2 –> -0.029

This means that for each of these variables, if they were fixed at the values they are set to, the change in average class prediction would be these large negative values.

  • Construct a SHAP graph and interpret it.
rf_shap_2 <-predict_parts(explainer = rf_explain,
                        new_observation = obs2_q5,
                        type = "shap",
                        B = 20 
)

plot(rf_shap_2)

Does it tell a similar story to the break-down plot?

We can see that SEVERAL variables have boxplots of their effects straddling zero spreading across both positive and negative values.

These variables set to their appropriate constants include:

total_bal_il

int_rate

sub_grade

num_il_tl

emp_length

total_il_high_credit_limit

This means that when we changed the order of considering these variables and re-run the breakdown test 20 times, we have the variables having multiple instances of both negative and positive effects.

This narrative is in direct conflict with the clear cut picture we were painted above about variable effects.

  • Construct a LIME graph (follow my code carefully).
model_type.dalex_explainer <- DALEXtra::model_type.dalex_explainer
predict_model.dalex_explainer <- DALEXtra::predict_model.dalex_explainer

set.seed(2)
lime_rf_2 <- predict_surrogate(explainer = rf_explain,
                             new_observation = obs2_q5 %>%
                               select(-Class), 
                             n_features = 5,
                             n_permutations = 1000,
                             type = "lime")

lime_rf_2 %>% 
  select(model_r2, model_prediction, prediction) %>% 
  distinct()
plot(lime_rf_2) +
  labs(x = "Variable")

How close is each original prediction to the prediction from the local model? Interpret the result. You can also try using fewer or more variables in the local model than I used in the example.

Prediction from Original RF Model: 0.74

Prediction from Local Model: 0.82 (+0.08)

The predictions are pretty close…the Local Model is +0.08…

– They are basically the same because most variables have roughly the same impact… probably because the total_il_high_credit_limit variable appears to have slightly greater impact here and some variables impacts may be on the higher end of their boxplots…also just so happens 3 / 5 vars have positive impacts of all same size….

Local Model R^2: 0.05 – Not Good

Bar Plot Shows Variable Importance From Local Model:

– annual income nd total bal il have big negative effects

– the rest appear to have equally as high impact vars but positive

OBSERVATION 20

obs20 <- lending_test %>% 
  slice(20)
obs20
  • Construct a break-down plot using the default ordering.
pp_rf_20 <- predict_parts(explainer = rf_explain,
                          new_observation = obs20,
                          type = "break_down") 

plot(pp_rf_20)

Interpret the resulting graph. Which variables contribute most to each observation’s prediction?

First we can see the initial bars start at the intercept of 0.848 which marks the value of the average predicted class (1 = good, 0 = bad) when applying the rf model to the training data.

We can see we end up 0.018 lower in our predicted class of paying back a loan than the intercept. This very little change is due to the fact that a lot of the variables’ effects have cancelling effects (fairly even split of + and - effects). The resulting 0.83 is a strong good prediction.

We can see that the largest tugs come from variables both + and - (and I guess that makes them most influential as they cancel each other out for the most part)…:

int_rate == 12.99 –> -0.04

total_bal_il == 26275 –> 0.037

inq_fi == 4 –> -0.028

This means that for each of these variables, if they were fixed at the values they are set to, the change in average class prediction would be their outputted vals.

This was a weird one to eval contribution: 1) do the bigger tugs still count as most significant because they are cancelling eachother out or 2) do the small negative tugs count more even though they wouldn’t matter if the big tugs didn’t cancel out????

  • Construct a SHAP graph and interpret it.
rf_shap_20 <-predict_parts(explainer = rf_explain,
                        new_observation = obs20,
                        type = "shap",
                        B = 20 
)

plot(rf_shap_20)

Does it tell a similar story to the break-down plot?

We can see that SEVERAL variables have boxplots of their effects straddling zero spreading across both positive and negative values.

These variables set to their appropriate constants include:

total_bal_il

sub_grade

annual_inc

int_rate

addr_state

all_util

inq_last_6mths

This means that when we changed the order of considering these variables and re-run the breakdown test 20 times, we have almost every single variable having multiple instances of both negative and positive effects.

This narrative is in direct conflict with the clear cut picture we were painted above as the vars effects are not as clear as we thought above.

  • Construct a LIME graph (follow my code carefully).
model_type.dalex_explainer <- DALEXtra::model_type.dalex_explainer
predict_model.dalex_explainer <- DALEXtra::predict_model.dalex_explainer

set.seed(2)
lime_rf_20 <- predict_surrogate(explainer = rf_explain,
                             new_observation = obs20 %>%
                               select(-Class), 
                             n_features = 5,
                             n_permutations = 1000,
                             type = "lime")

lime_rf_20 %>% 
  select(model_r2, model_prediction, prediction) %>% 
  distinct()
plot(lime_rf_20) +
  labs(x = "Variable")

How close is each original prediction to the prediction from the local model? Interpret the result. You can also try using fewer or more variables in the local model than I used in the example.

Prediction from Original RF Model: 0.83

Prediction from Local Model: 0.8 (-0.03)

The predictions are fairly close…the Local Model is -0.03…

– This likely is because of the massive negative impact of revol_util and annual_inc which pushed the pred slightly below as they countered the pos impact from others. Annual Income’s effects appear to be much more negative here than above…

Local Model R^2: 0.07 – Terrible

Bar Plot Shows Variable Importance From Local Model:

– revol_util has a massive negative impact

– annual income has equally massive negative impact

– inq last 6 mths and total il high credit have moderately large positive impacts

OBSERVATION 200

obs200 <- lending_test %>% 
  slice(200)
obs200
  • Construct a break-down plot using the default ordering.
pp_rf_200 <- predict_parts(explainer = rf_explain,
                          new_observation = obs200,
                          type = "break_down") 

plot(pp_rf_200)

Interpret the resulting graph. Which variables contribute most to each observation’s prediction?

First we can see the initial bars start at the intercept of 0.848 which marks the value of the average predicted class (1 = good, 0 = bad) when applying the rf model to the training data.

We can see we end up 0.132 HIGHER in our predicted class of paying back a loan than the intercept thanks to some big positive jumps from influential variables. The resulting 0.98 is an extremely strong good prediction.

We can see that the largest positive jumps from variables come from:

int_rate == 13.44 –> 0.029

sub_grade == 13 –> 0.029

total_bal_il == 0 –> 0.028

and also it helps that all but 3 in this case have positive impacts…

This means that for each of these variables, if they were fixed at the values they are set to, the change in average class prediction would be these large positive values which pulled our prediction for this observation up very high.

  • Construct a SHAP graph and interpret it.
rf_shap_200 <-predict_parts(explainer = rf_explain,
                        new_observation = obs200,
                        type = "shap",
                        B = 20 
)

plot(rf_shap_200)

Does it tell a similar story to the break-down plot?

We can see that SEVERAL variables have boxplots of their effects straddling zero spreading across both positive and negative values.

These variables set to their appropriate constants include:

total_bal_il

sub_grade

int_rate

total_il_high_credit_limit

annual_inc

num_il_til

This means that when we changed the order of considering these variables and re-run the breakdown test 20 times, we have these variables having multiple instances of both negative and positive effects.

This narrative is in direct conflict with the clear cut picture we were painted above as we see the impacts of vars are not as obvious.

  • Construct a LIME graph (follow my code carefully).
model_type.dalex_explainer <- DALEXtra::model_type.dalex_explainer
predict_model.dalex_explainer <- DALEXtra::predict_model.dalex_explainer

set.seed(2)
lime_rf_200 <- predict_surrogate(explainer = rf_explain,
                             new_observation = obs200 %>%
                               select(-Class), 
                             n_features = 5,
                             n_permutations = 1000,
                             type = "lime")

lime_rf_200 %>% 
  select(model_r2, model_prediction, prediction) %>% 
  distinct()
plot(lime_rf_200) +
  labs(x = "Variable")

How close is each original prediction to the prediction from the local model? Interpret the result. You can also try using fewer or more variables in the local model than I used in the example.

Prediction from Original RF Model: 0.98

Prediction from Local Model: 0.81 (-0.17)

The predictions are not close…the Local Model is -0.17…

– This likely is because here we have massive negative impacts seen from revol_util and annual_income, much higher than displayed above, which account for the massive drop in predicted class score.

Local Model R^2: 0.06 – Awful

Bar Plot Shows Variable Importance From Local Model:

– revol_util, annual income have massively large negative impacts that outpace the positive impacts of inq last 6mths, inq_fi, and total_bal_il.

6. Describe how you would use the interpretable machine learning tools we’ve learned (both local and global) in future machine learning projects? How does each of them help you?

I could find great use in using the boxplot and histogram of residuals for a model from global interpretable ML and from local interpretable ML I would definitely have great value from using breakdown profiles and shapley plots.

For Global: Visualizing Residuals

This past Summer, I had an internship with a Fintech company called AvidXChange, a Charlotte, NC based Accounts Payable Automation company. I used sql to pull data from databases and machine learning skills to use neural networks and deep learning. We wanted to more accurately predicted which of our clients accepted Virtual Credit Card and which didn’t as this was a big problem. Avid makes most of their money from fees on VCC cards being used to pay off invoices as opposed to just direct deposit (this is more ideal for most as they get the money much quicker despite a very small fee). If able to better identify who simply can’t take VCC, it would save a lot of time and money better used on other clients.

When we finalized the mode, I would have loved to see how well it predicted through seeing the distribution of its residuals. My model also was a class prediction like w the lending data, specifically using the spelling and make up of the company name to predict if they took VCC (the hunch was that companies that appeared to be people did not accept VCC and the model used a series of dictionaries for letters and names from census data to distinguish names).

If I could analyze the residuals a bit more with things like the boxplot and histogram like we did above, I would have been able to see a bit better how well we predicted correctly. Specially, I would have been able to see if it was a good model beyond simply looking at output metrics like I did. If I could have visualized the residuals and seen how it tended to predict the majority class more than most other things, that would have been very easy to see and cool to help show the results as opposed to simply telling people the results with metrics filled with jargon.

For Local: Breakdown Profiles and Shapley

Also, in my internship I added on to a VCC model a propensity score value which predicted a probability at which a company (we thought) would accept VCC. It took in many variables including past invoices, size of the invoice, size of the company, if they have taken VCC in the past before, location…

I would have loved to see for specific companies which variables were more significant in driving the propensity scores to where they went. Obviously Panera takes VCC because they take credit card but what variable specifically was honing in on this?

Seeing the little tug of war between variables and how they effected the outcome woud have been really cool. Additionally, I would have liked to see the permanence of these variables effects beyond just one observation. If I could have also used a shap plot, I could have seen how firm these variable impacts were as well and seen how it changed from large to small company occurrences.

7. Save this final model using the write_rds() function - see the section of the tidymodels intro for a similar example, but we’re using write_rds() instead of saveRDS(). We are going to use the model in the next part. You’ll want to save it in the folder where you create your shiny app. Run the code, and then add eval=FALSE to the code chunk options (next to the r inside the curly brackets) so it doesn’t rerun this each time you knit.

# # finalize model
# rf_final <- rf_workflow %>% 
#   finalize_workflow(best_accuracy) %>% 
#   fit(data = lending_training)


write_rds(rf_final, "rf_final.rds")

rf_final_read <- readRDS("rf_final.rds")

Shiny app – DONE

My App: https://ajp28117.shinyapps.io/Lending_Club/

Repo for App: https://github.com/apalma127/lending_small

Website Post: https://anthonypalma.netlify.app/posts/shinyapps/ (~~ scroll down to lending data shiny app ~~)

Data Ethics: Data visualization principles – DONE

Were there any principles mentioned that you hadn’t heard of before?

I was unaware that the y axis on line graphs shouldn’t include 0 and that the graph should be much more honed in on the region of interest than zoomed out. I actually have never done this to be honest just because I thought it was good etiquette to always start at 0…

What graph stood out for you as “the worst”?

The worst has to be the bubble chart with the professor in front. While it does have a lot of data and cool things going on, it is a mess. With no axes and random size differences, there is so much going on the only thing you can see is relative difference. Additionally, there is a lot of blobbing of points on top of each other making even relative difference difficult to see.

Did any of the graphs fool you?

The gun deaths in FL graph was veryyyyy confusing because of the upside down filling of it. At first I was like wow super weird stand your ground DECREASED gun deaths but that was simply because the creator of the graph wanted you to see quite the opposite of reality.

How does practicing good data visualization principles fit in with data ethics?

The florida gun deaths one was a really good example of how bad data practices, no matter the intent, can actually manipulate the truth and lie to the viewer. Such fake news would be damaging to a narrative in an election especially because of how contrary to reality it is. If someone were to do this during election time with important stats about abortion and immigration, it would be crucial to changing minds and votes.

LS0tCnRpdGxlOiAnQXNzaWdubWVudCAjNScKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpCmBgYAoKYGBge3IgbGlicmFyaWVzfQojIFNFRSBtb2RlbGRhdGEgcGFja2FnZSBmb3IgbmV3IGRhdGFzZXRzCmxpYnJhcnkodGlkeXZlcnNlKSAgICAgICAgICMgZm9yIGdyYXBoaW5nIGFuZCBkYXRhIGNsZWFuaW5nCmxpYnJhcnkodGlkeW1vZGVscykgICAgICAgICMgZm9yIG1vZGVsaW5nCmxpYnJhcnkocmFuZ2VyKSAgICAgICAgICAgICMgZm9yIHJhbmRvbSBmb3Jlc3QgLSB3aWxsIG5lZWQgZm9yIHNoaW55IGFwcApsaWJyYXJ5KGx1YnJpZGF0ZSkgICAgICAgICAjIGZvciBkYXRlIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KHRoZW1pcykgICAgICAgICAgICAjIGZvciB1cCBhbmQgZG93bnNhbXBsaW5nCmxpYnJhcnkoREFMRVgpICAgICAgICAgICAgICMgZm9yIG1vZGVsIGludGVycHJldGF0aW9uICAKbGlicmFyeShEQUxFWHRyYSkgICAgICAgICAgIyBmb3IgZXh0ZW5zaW9uIG9mIERBTEVYCmxpYnJhcnkobGltZSkKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoKSkgIyBMaXNhJ3MgZmF2b3JpdGUgdGhlbWUKYGBgCgpgYGB7ciBkYXRhfQpkYXRhKCJsZW5kaW5nX2NsdWIiKQojIERhdGEgZGljdGlvbmFyeSAoYXMgY2xvc2UgYXMgSSBjb3VsZCBmaW5kKTogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS93b3Jkc2ZvcnRoZXdpc2UvbGVuZGluZy1jbHViL2Rpc2N1c3Npb24vMTcwNjkxCmBgYAoKCiMjIFB1dCBpdCBvbiBHaXRIdWIhIC0tIERPTkUgICAgICAgIAoKCkdpdGh1YiBSZXBvIExpbms6IGh0dHBzOi8vZ2l0aHViLmNvbS9hcGFsbWExMjcvYXNzaWdubWVudC01CgoKIyMgSW50ZXJwcmV0YWJsZSBNTCBtZXRob2RzIC0tIERPTkUKCldlIHdpbGwgb25jZSBhZ2FpbiB1c2UgdGhlIGxlbmRpbmcgY2x1YiBkYXRhIHRoYXQgd2UgdXNlZCBpbiB0aGUgM3JkIGFzc2lnbm1lbnQuIFdlIHdpbGwgZm9jdXMgb24gdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwsIHdoaWNoIEkgcmVjcmVhdGUgYmVsb3cuIChOb3RlIHdlIHVzZSB0aGlzIG1vZGVsIGV2ZW4gdGhvdWdoIHRoZSB0cnVlIG5lZ2F0aXZlIHJhdGUgb2YgdGhlIHRyYWluaW5nIHNldCBpcyBxdWl0ZSBiYWQuKQoKYGBge3J9CnNldC5zZWVkKDQ5NCkgIyBmb3IgcmVwcm9kdWNpYmlsaXR5Cgojc3BsaXQgZGF0YQpsZW5kaW5nX3NwbGl0IDwtIGluaXRpYWxfc3BsaXQobGVuZGluZ19jbHViLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcCA9IC43NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmF0YSA9IENsYXNzKQoKbGVuZGluZ190cmFpbmluZyA8LSB0cmFpbmluZyhsZW5kaW5nX3NwbGl0KQpsZW5kaW5nX3Rlc3QgPC0gdGVzdGluZyhsZW5kaW5nX3NwbGl0KQoKCiNjcmVhdGUgcmVjaXBlIC0gaW5jbHVkaW5nIHVwIGFuZCBkb3duc2FtcGxpbmcgZm9yIG1vZGVsIGZpdHRpbmcKc2V0LnNlZWQoNDU2KQpyZl9yZWNpcGUgPC0gCiAgcmVjaXBlKENsYXNzIH4gLiwKICAgICAgICAgZGF0YSA9IGxlbmRpbmdfdHJhaW5pbmcpICU+JSAKICBzdGVwX3Vwc2FtcGxlKENsYXNzLCBvdmVyX3JhdGlvID0gLjUpICU+JSAKICBzdGVwX2Rvd25zYW1wbGUoQ2xhc3MsIHVuZGVyX3JhdGlvID0gMSkgJT4lIAogIHN0ZXBfbXV0YXRlX2F0KGFsbF9udW1lcmljKCksIAogICAgICAgICAgICAgICAgIGZuID0gfmFzLm51bWVyaWMoLikpCgojIGNyZWF0ZSBtb2RlbApyZl9tb2RlbCA8LSAKICByYW5kX2ZvcmVzdChtdHJ5ID0gdHVuZSgpLCAKICAgICAgICAgICAgICBtaW5fbiA9IHR1bmUoKSwgCiAgICAgICAgICAgICAgdHJlZXMgPSAxMDApICU+JSAKICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKSAlPiUgCiAgc2V0X2VuZ2luZSgicmFuZ2VyIikKCiMgY3JlYXRlIHdvcmtmbG93CnJmX3dvcmtmbG93IDwtCiAgd29ya2Zsb3coKSAlPiUgCiAgYWRkX3JlY2lwZShyZl9yZWNpcGUpICU+JSAKICBhZGRfbW9kZWwocmZfbW9kZWwpCgogIGdyaWRfcmVndWxhcihmaW5hbGl6ZShtdHJ5KCksCiAgICAgICAgICAgICAgICAgICAgICAgIGxlbmRpbmdfdHJhaW5pbmcgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KC1DbGFzcykpLAogICAgICAgICAgICAgICBtaW5fbigpLAogICAgICAgICAgICAgICBsZXZlbHMgPSAzKQoKIyBjcmVhdGUgcGVuYWx0eSBncmlkCiAgcmZfcGVuYWx0eV9ncmlkIDwtIApncmlkX3JlZ3VsYXIoZmluYWxpemUobXRyeSgpLAogICAgICAgICAgICAgICAgICAgICAgICBsZW5kaW5nX3RyYWluaW5nICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtQ2xhc3MpKSwKICAgICAgICAgICAgICAgbWluX24oKSwKICAgICAgICAgICAgICAgbGV2ZWxzID0gMykKCgojIGNyZWF0ZSBjdiBzYW1wbGVzCnNldC5zZWVkKDQ5NCkgI2ZvciByZXByb2R1Y2libGUgNS1mb2xkCmxlbmRpbmdfY3YgPC0gdmZvbGRfY3YobGVuZGluZ190cmFpbmluZywKICAgICAgICAgICAgICAgICAgICAgICB2ID0gNSkKCiMgdHVuZSBtb2RlbApyZl90dW5lIDwtIAogIHJmX3dvcmtmbG93ICU+JSAKICB0dW5lX2dyaWQoCiAgICByZXNhbXBsZXMgPSBsZW5kaW5nX2N2LAogICAgZ3JpZCA9IHJmX3BlbmFsdHlfZ3JpZAogICkKCiMgZmluZCBtb2RlbCB3aXRoIGJlc3QgYWNjdXJhY3kKYmVzdF9hY2N1cmFjeSA8LQogIHJmX3R1bmUgJT4lIAogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpCgojIGZpbmFsaXplIG1vZGVsCnJmX2ZpbmFsIDwtIHJmX3dvcmtmbG93ICU+JSAKICBmaW5hbGl6ZV93b3JrZmxvdyhiZXN0X2FjY3VyYWN5KSAlPiUgCiAgZml0KGRhdGEgPSBsZW5kaW5nX3RyYWluaW5nKQpgYGAKCioqMS4gVXNlIGZ1bmN0aW9ucyBmcm9tIHRoZSBgREFMRVhgIGFuZCBgREFMRVh0cmFgIGxpYnJhcmllcyB0byBjcmVhdGUgYSBoaXN0b2dyYW0gYW5kIGJveHBsb3Qgb2YgdGhlIHJlc2lkdWFscyBmcm9tIHRoZSB0cmFpbmluZyBkYXRhLioqIAoKCmBgYHtyfQpyZl9leHBsYWluIDwtIAogIGV4cGxhaW5fdGlkeW1vZGVscygKICAgIG1vZGVsID0gcmZfZmluYWwsCiAgICBkYXRhID0gbGVuZGluZ190cmFpbmluZyAlPiUgc2VsZWN0KC1DbGFzcyksIAogICAgeSA9IGxlbmRpbmdfdHJhaW5pbmcgJT4lIAogICAgICBtdXRhdGUoQ2xhc3NfbnVtID0gYXMuaW50ZWdlcihDbGFzcyA9PSJnb29kIikpICU+JSAKICAgICAgcHVsbChDbGFzc19udW0pLAogICAgbGFiZWwgPSAicmYiCiAgKQoKYGBgCgoKYGBge3J9CnJmX21vZF9wZXJmIDwtICBtb2RlbF9wZXJmb3JtYW5jZShyZl9leHBsYWluKQoKcmZfbW9kX3BlcmYKYGBgCgoKCgpgYGB7cn0KaGlzdF9wbG90IDwtIAogIHBsb3QocmZfbW9kX3BlcmYsCiAgICAgICBnZW9tID0gImhpc3RvZ3JhbSIpCgoKYm94X3Bsb3QgPC0KICBwbG90KHJmX21vZF9wZXJmLAogICAgICAgZ2VvbSA9ICJib3hwbG90IikKCmBgYAoKCmBgYHtyfQpoaXN0X3Bsb3QKYGBgCgpgYGB7cn0KYm94X3Bsb3QKYGBgCgoKCioqSG93IGRvIHRoZXkgbG9vaz8gQW55IGludGVyZXN0aW5nIGJlaGF2aW9yPyoqCgpUaGV5IGFyZSBub3QgY2VudGVyZWQgYXJvdW5kIDAgYXMgeW91IHdvdWxkIHdhbnQgYSBnb29kIG1vZGVsIHRvIGRvLiBUaGV5IGFwcGVhciB0byBza2V3IHJpZ2h0IGhlYXZpbHkgd2VsbCBpbnRvIHRoZSBwb3NpdGl2ZXMgaW5kaWNhdGluZyBhIGNvbnRpbnVvdXMgdW5kZXJwcmVkaWN0aW9uLiAgVGhpcyBpcyB2ZXJ5IHNpZ25pZmljYW50IGFzIGl0IHNob3dzIGEgcGF0dGVybiBvZiB1bmRlcnByZWRpY3Rpb25zIHdoaWNoIHNob3VsZG4ndCBiZSBhIGNvbnN0YW50IHBhdHRlcm4gaWYgaXQgaXMgYSBnb29kIG1vZGVsLiAKCgoqKjIuIFVzZSBgREFMRVhgIGZ1bmN0aW9ucyB0byBjcmVhdGUgYSB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QgZnJvbSB0aGlzIG1vZGVsLioqIAoKYGBge3J9CnJmX3Zhcl9pbXAgPC0gCiAgbW9kZWxfcGFydHMoCiAgICByZl9leHBsYWluCiAgICApCgpwbG90KHJmX3Zhcl9pbXAsIHNob3dfYm94cGxvdHMgPSBUUlVFKQpgYGAKCgoKKipXaGF0IGFyZSB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzPyoqCgpUaGUgbW9zdCBpbXBvcnRhbm50IHZhcmlhYmxlcyB1c2VkIHRvIGhlbHAgY29ycmVjdGx5IHByZWRpY3QgaWYgYSBsb2FuIHdhcyBwYWlkIGJhY2sgd2FzICoqaW50ZXJlc3QgcmF0ZSoqLCAqKnN1Yl9ncmFkZSoqLCAqKm9wZW5faWxfMjRtKiosIGFuZCAqKmFubnVhbCBpbmNvbWUqKi4gIFRoZXNlIGFyZSBub3Qgc3VycHJpc2luZyBhdCBhbGwgYmVjYXVzZSBtb3N0IG9mIHRoZW0gcmVsYXRlIGV4YWN0bHkgYmFjayB0byBhZmZlY3Rpbmcgb25lJ3MgYWJpbGl0eSB0byByZXBheSAuLi4gaWUgaWYgYW4gaW50ZXJlc3QgcmF0ZSBpcyBpbmNyZWRpYmx5IGhpZ2ggaXQgbWFrZXMgdGhlIHBheSBiYWNrIGV4dHJlbWVseSBsZXNzIGxpa2VseSwgaWYgb25lIGhhcyBhIHZlcnkgbG93IGluY29tZSBpdCBtYWtlcyBwYXliYWNrIHZlcnkgZGlmZmljdWx0Li4uLgoKCioqMy4gV3JpdGUgYSBmdW5jdGlvbiBjYWxsZWQgYGNwX3Byb2ZpbGVgIHRvIG1ha2UgYSBDUCBwcm9maWxlLioqIAoKVGhlIGZ1bmN0aW9uIHdpbGwgdGFrZSBhbiBleHBsYWluZXIsIAphIG5ldyBvYnNlcnZhdGlvbiwgCmFuZCBhIHZhcmlhYmxlIG5hbWUgYXMgaXRzIGFyZ3VtZW50cyBhbmQgCgpjcmVhdGUgYSBDUCBwcm9maWxlIGZvciBhIHF1YW50aXRhdGl2ZSBwcmVkaWN0b3IgdmFyaWFibGUuIAoKCllvdSB3aWxsIG5lZWQgdG8gdXNlIHRoZSBgcHJlZGljdF9wcm9maWxlKClgIGZ1bmN0aW9uIGluc2lkZSB0aGUgZnVuY3Rpb24geW91IGNyZWF0ZSAtIApwdXQgdGhlIHZhcmlhYmxlIG5hbWUgdGhlcmUgc28gdGhlIHBsb3R0aW5nIHBhcnQgaXMgZWFzaWVyLgoKCmBgYHtyfQpvYnMyIDwtIGxlbmRpbmdfdHJhaW5pbmcgJT4lIAogIHNsaWNlKDIpCm9iczIKYGBgCgoqKkNPREUgVEVTVCBGT1IgQU5OVUFMIElOQyBBTkQgSU5UIFJBVEUqKgoKYGBge3J9CmlzLmludGVnZXIobGVuZGluZ190cmFpbmluZyRhbm51YWxfaW5jKQppcy5pbnRlZ2VyKGxlbmRpbmdfdHJhaW5pbmckaW50X3JhdGUpCgpgYGAKCgoqKlRFU1QgMSoqCgoKYGBge3J9CmNwX3Byb2ZpbGVfdGVzdCA8LSBwcmVkaWN0X3Byb2ZpbGUoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3X29ic2VydmF0aW9uID0gb2JzMiwKICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXMgPSBjKCJhbm51YWxfaW5jIiwgImludF9yYXRlIikpCgoKY3BfcHJvZmlsZV90ZXN0CmBgYAoKYGBge3J9CmNwX3Byb2ZpbGVfdGVzdCAlPiUgCiAgZmlsdGVyKGBfdm5hbWVfYCAlaW4lIGMoImFubnVhbF9pbmMiKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGFubnVhbF9pbmMsCiAgICAgICAgICAgICB5ID0gYF95aGF0X2ApKSArCiAgZ2VvbV9saW5lKCkgCmBgYAoKKipXaXRoIHlfaGF0IGJlaW5nIG91ciBwcmVkaWN0ZWQgY2xhc3Mgb3V0Y29tZSAod2l0aCAxID0gZ29vZCBwYXkgYmFjayBvbiB0aW1lIGFuZCAwID0gYmFkIG5vdCBwYWlkIGJhY2sgb24gdGltZSkgaXQgYXBwZWFycyB0byBzaG93IHVzOiBhcyBpbmNvbWUgaW5jcmVhc2VzLCB3aGlsZSBpdCBkb2Vzbid0IGFwcGVhciB0byBiZSBhIG1ham9yIGRpZmZlcmVuY2UsIGl0IGlzIHNsaWdodGx5IG1vcmUgbGlrZWx5IHRoZSBwZXJzb24gaXMgbm90IG9uIHRpbWUgdyByZXBheW1lbnQuICBZZXQsIGl0IGFwcGVhcnMgaXQgaXMgbXVjaCBtb3JlIGNvbnN0YW50IHcgdGhlIHRyZW5kIGxpbmUgZm9yIGhpZ2hlciBpbmNvbWUgd2hlcmVhcyBsb3dlciBpbmNvbWUgaXMgbXVjaCBtb3JlIGNoYW90aWMgaW4gcHJlZGljdGVkIHBheWJhY2suKiogCgoKKipURVNUIDIqKgoKCmBgYHtyfQpjcF9wcm9maWxlX3Rlc3QgJT4lIAogIGZpbHRlcihgX3ZuYW1lX2AgJWluJSBjKCJpbnRfcmF0ZSIpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gaW50X3JhdGUsCiAgICAgICAgICAgICB5ID0gYF95aGF0X2ApKSArCiAgZ2VvbV9saW5lKCkgCmBgYAoKKipXaXRoIHlfaGF0IGJlaW5nIG91ciBwcmVkaWN0ZWQgY2xhc3Mgb3V0Y29tZSAod2l0aCAxID0gZ29vZCBwYXkgYmFjayBvbiB0aW1lIGFuZCAwID0gYmFkIG5vdCBwYWlkIGJhY2sgb24gdGltZSkgaXQgYXBwZWFycyB0byBzaG93IHVzIGEgdmVyeSBvYnZpb3VzIHRyZW5kLiAgQXMgZXhwZWN0ZWQsIGFzIHRoZSBpbnRlcmVzdCByYXRlIGluY3JlYXNlcyBtZWFuaW5nIGluY3JlYXNpbmdseSBtb3JlIG1vbmV5IGlzIG93ZWQgYmFjayB0aGFuIGJvcnJvd2VkLCB0aGUgYWJpbGl0eSB0byBwYXkgb24gdGltZSBhbmQgdGhlIHByZWRpY3RlZCBjbGFzcyB2YWx1ZSByYWNlcyBkb3dud2FyZCBjbG9zZXIgdG8gYmFkIG1lYW5pbmcgdGhlIHBheW1lbnQgaXMgbXVjaCBsZXNzIGxpa2VseSB0byBiZSBvbiB0aW1lLioqIAoKCgoqKk5vdyBGdW5jdGlvbiBXcml0aW5nIHRpbWUuLi4qKiAKCldyaXRlIGEgZnVuY3Rpb24gY2FsbGVkIGBjcF9wcm9maWxlYCB0byBtYWtlIGEgQ1AgcHJvZmlsZS4gCgpUaGUgZnVuY3Rpb24gd2lsbCB0YWtlIGFuIGV4cGxhaW5lciwgCmEgbmV3IG9ic2VydmF0aW9uLCAKYW5kIGEgdmFyaWFibGUgbmFtZSBhcyBpdHMgYXJndW1lbnRzIGFuZCAKCmNyZWF0ZSBhIENQIHByb2ZpbGUgZm9yIGEgcXVhbnRpdGF0aXZlIHByZWRpY3RvciB2YXJpYWJsZS4gCgoKWW91IHdpbGwgbmVlZCB0byB1c2UgdGhlIGBwcmVkaWN0X3Byb2ZpbGUoKWAgZnVuY3Rpb24gaW5zaWRlIHRoZSBmdW5jdGlvbiB5b3UgY3JlYXRlIC0gCnB1dCB0aGUgdmFyaWFibGUgbmFtZSB0aGVyZSBzbyB0aGUgcGxvdHRpbmcgcGFydCBpcyBlYXNpZXIuCgoKWW91J2xsIGFsc28gd2FudCB0byB1c2UgYC5kYXRhW1tdXWAgcmF0aGVyIHRoYW4gYGFlcygpYCBhbmQgcXVvdGUgdGhlIHZhcmlhYmxlcy4gVXNlIHRoZSBgY3BfcHJvZmlsZSgpYCBmdW5jdGlvbiB0byBjcmVhdGUgb25lIENQIHByb2ZpbGUgb2YgeW91ciBjaG9vc2luZy4gCgpCZSBzdXJlIHRvIGNob29zZSBhIHZhcmlhYmxlIHRoYXQgaXMgbnVtZXJpYywgbm90IGludGVnZXIuIFRoZXJlIHNlZW0gdG8gYmUgaXNzdWVzIHdpdGggdGhvc2UgdGhhdCBJJ20gbG9va2luZyBpbnRvLgoKYGBge3J9CiBjcF9wcm9maWxlIDwtIGZ1bmN0aW9uKGV4cGxhaW5lcl9mbiwgbmV3X29icywgYHZhcmApIHsKICAKICAKICBwcm9maWxlIDwtIHByZWRpY3RfcHJvZmlsZShleHBsYWluZXIgPSBleHBsYWluZXJfZm4sIAogICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG5ld19vYnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGVzID0gYHZhcmApCiAgCiAgZ3JhcGggPC0gcHJvZmlsZSAlPiUgCiAgICBmaWx0ZXIoYF92bmFtZV9gICVpbiUgYyhgdmFyYCkpICU+JSAKICAgIGdncGxvdChhZXMoeCA9IC5kYXRhW1tgdmFyYF1dLCB5ID0gYF95aGF0X2ApKSArCiAgICBnZW9tX2xpbmUoKSAKCiAgCiAgZ3JhcGgKIH0KCmBgYAoKCmBgYHtyfQpsZW5kaW5nX251bWVyaWMgPC0gc2VsZWN0X2lmKGxlbmRpbmdfdHJhaW5pbmcsIGlzLm51bWVyaWMpICAgICAgICAgICAgCgpsZW5kaW5nX251bWVyaWMKYGBgCgpgYGB7cn0KY3BfcHJvZmlsZShyZl9leHBsYWluLCBvYnMyLCAiYW5udWFsX2luYyIpCmBgYAoKYGBge3J9CmNwX3Byb2ZpbGUocmZfZXhwbGFpbiwgb2JzMiwgImludF9yYXRlIikKYGBgCgpgYGB7cn0KY3BfcHJvZmlsZShyZl9leHBsYWluLCBvYnMyLCAiZnVuZGVkX2FtbnQiKQoKYGBgCgpgYGB7cn0KY3BfcHJvZmlsZShyZl9leHBsYWluLCBvYnMyLCAicmV2b2xfdXRpbCIpCgpgYGAKCgoqKjQuIFVzZSBgREFMRVhgIGZ1bmN0aW9ucyB0byBjcmVhdGUgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3RzICh3aXRoIHRoZSBDUCBwcm9maWxlcyBpbiBncmF5KSBmb3IgdGhlIDMtNCBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXMuKiogCgoKSWYgdGhlIGltcG9ydGFudCB2YXJpYWJsZXMgYXJlIGNhdGVnb3JpY2FsLCB5b3UgY2FuIGluc3RlYWQgbWFrZSBhIENQIHByb2ZpbGUgZm9yIDMgb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhc2V0IGFuZCBkaXNjdXNzIGhvdyB5b3UgY291bGQgZ28gYWJvdXQgY29uc3RydWN0aW5nIGEgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3QgZm9yIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgKHlvdSBkb24ndCBoYXZlIHRvIGNvZGUgaXQsIGJ1dCB5b3UgY2FuIGlmIHlvdSB3YW50IGFuIGV4dHJhIGNoYWxsZW5nZSkuCgpNb3N0IEltcG9ydGFudDogICoqaW50ZXJlc3QgcmF0ZSoqLCAqKnN1Yl9ncmFkZSoqLCAqKm9wZW5faWxfMjRtKiosIGFuZCAqKmFubnVhbCBpbmNvbWUqKgoKYGBge3J9CmlzLmZhY3RvcihsZW5kaW5nX3RyYWluaW5nJGludF9yYXRlKQpgYGAKYGBge3J9CmlzLmZhY3RvcihsZW5kaW5nX3RyYWluaW5nJHN1Yl9ncmFkZSkKCmBgYAoKYGBge3J9CmlzLmZhY3RvcihsZW5kaW5nX3RyYWluaW5nJG9wZW5faWxfMjRtKQoKYGBgCgpgYGB7cn0KaXMuZmFjdG9yKGxlbmRpbmdfdHJhaW5pbmckYW5udWFsX2luYykKCmBgYAoKCgpgYGB7cn0KcmZfcGRwIDwtIG1vZGVsX3Byb2ZpbGUoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9IGMoImludF9yYXRlIiwgIm9wZW5faWxfMjRtIiwgImFubnVhbF9pbmMiKSkKCnBsb3QocmZfcGRwLCAKICAgICB2YXJpYWJsZXMgPSBjKCJpbnRfcmF0ZSIsICJvcGVuX2lsXzI0bSIsICJhbm51YWxfaW5jIiksCiAgICAgZ2VvbSA9ICJwcm9maWxlcyIpCmBgYAoKCioqRGlzY3VzcyBob3cgeW91IGNvdWxkIGdvIGFib3V0IGNvbnN0cnVjdGluZyBhIHBhcnRpYWwgZGVwZW5kZW5jZSBwbG90IGZvciBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlKioKCgotLSBTdWJncmFkZSA9IENhdGVnb3JpY2FsIAoKLS0gU3RlcHMgdG8gQ3JlYXRlIFBEUCBmb3Igc3ViZ3JhZGUKCisrKysgb25lIG1ham9yIHN0ZXAgdG8gZG8gYW5kIHRoZW4gY2FuIHVzZSBjb2RlIGFib3ZlOiBjb252ZXJ0IGZyb20gY2F0ZWdvcmljYWwgLyBmYWN0b3IgdG8gbnVtZXJpYyB2YWx1ZSAhISEKCi0tIGZvciBlYWNoIGxldmVsIG9mIHN1YmdyYWRlLCB3b3JraW5nIGZyb20gdG9wIHRvIGJvdHRvbSwgYXNzaWduIGEgbnVtZXJpYyB2YWx1ZSB1c2luZyBhIHNlcmllcyBvZiBpZmVsc2Ugc3RhdGVtZW50cyB3aXRoaW4gbXV0YXRlIGZvciBzdWJncmFkZSB3aXRoaW4gbGVuZGluZyB0cmFpbmluZwoKLS0gbm93LCB3IHN1YmdyYWRlIGJlaW5nIG51bWVyaWMsIGNhbiBmZWVkIHJpZ2h0IGludG8gdGhlIGxpc3Qgb2YgdmFyaWFibGVzIGFib3ZlLi4uCgoKKio1LiBDaG9vc2UgMyBvYnNlcnZhdGlvbnMgYW5kIGRvIHRoZSBmb2xsb3dpbmcgZm9yIGVhY2ggb2JzZXJ2YXRpb246KiogIAoKCioqT0JTRVJWQVRJT04gMioqCgpgYGB7cn0Kb2JzMl9xNSA8LSBsZW5kaW5nX3Rlc3QgJT4lIAogIHNsaWNlKDIpCm9iczJfcTUKYGBgCgoKICAtIENvbnN0cnVjdCBhIGJyZWFrLWRvd24gcGxvdCB1c2luZyB0aGUgZGVmYXVsdCBvcmRlcmluZy4gCiAgCmBgYHtyfQpwcF9yZl8yIDwtIHByZWRpY3RfcGFydHMoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyX3E1LAogICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiYnJlYWtfZG93biIpIAoKcGxvdChwcF9yZl8yKQpgYGAKICAKKipJbnRlcnByZXQgdGhlIHJlc3VsdGluZyBncmFwaC4gV2hpY2ggdmFyaWFibGVzIGNvbnRyaWJ1dGUgbW9zdCB0byBlYWNoIG9ic2VydmF0aW9uJ3MgcHJlZGljdGlvbj8qKgoKRmlyc3Qgd2UgY2FuIHNlZXRoZSBpbml0aWFsIGJhcnMgc3RhcnQgYXQgdGhlIGludGVyY2VwdCBvZiAwLjg0OCB3aGljaCBtYXJrcyB0aGUgdmFsdWUgb2YgdGhlIGF2ZXJhZ2UgcHJlZGljdGVkIGNsYXNzICgxID0gZ29vZCwgMCA9IGJhZCkgd2hlbiBhcHBseWluZyB0aGUgcmYgbW9kZWwgdG8gdGhlIHRyYWluaW5nIGRhdGEuICAKCldlIGNhbiBzZWUgd2UgZW5kIHVwIDAuMTA4IGxvd2VyIGluIG91ciBwcmVkaWN0ZWQgY2xhc3Mgb2YgcGF5aW5nIGJhY2sgYSBsb2FuIHRoYW4gdGhlIGludGVyY2VwdCB0aGFua3MgdG8gc29tZSBoZWF2eSBoaXR0aW5nIG5lZ2F0aXZlIHZhcmlhYmxlcy4gVGhlIHJlc3VsdGluZyAwLjc0IGlzIGEgZmFpcmx5IHN0cm9uZyBnb29kIHByZWRpY3Rpb24uICAKCldlIGNhbiBzZWUgdGhhdCB0aGUgbGFyZ2VzdCBhbmQgbW9zdCBpbmZsdWVudGlhbCB0dWdzIGZyb20gdmFyaWFibGVzIGRvd24gdG93YXJkcyB0aGUgYWN0dWFsIHByZWRpY3RlZCB2YWx1ZSBmcm9tIHRoZSBpbnRlcmNlcHQgY29tZXMgZnJvbToKCgp0b3RhbF9iYWxfaWwgPT0gNTU0NDUgLS0+IC0wLjA0OCAKCm51bV9pbF90bCA9PSAxMCAtLT4gLTAuMDMyCgpvcGVuX2lsXzI0bSA9PSAyIC0tPiAtMC4wMjkKCgpUaGlzIG1lYW5zIHRoYXQgZm9yIGVhY2ggb2YgdGhlc2UgdmFyaWFibGVzLCBpZiB0aGV5IHdlcmUgZml4ZWQgYXQgdGhlIHZhbHVlcyB0aGV5IGFyZSBzZXQgdG8sIHRoZSBjaGFuZ2UgaW4gYXZlcmFnZSBjbGFzcyBwcmVkaWN0aW9uIHdvdWxkIGJlIHRoZXNlIGxhcmdlIG5lZ2F0aXZlIHZhbHVlcy4gIAogIAogIAogIC0gQ29uc3RydWN0IGEgU0hBUCBncmFwaCBhbmQgaW50ZXJwcmV0IGl0LiAKICAKYGBge3J9CnJmX3NoYXBfMiA8LXByZWRpY3RfcGFydHMoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgbmV3X29ic2VydmF0aW9uID0gb2JzMl9xNSwKICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzaGFwIiwKICAgICAgICAgICAgICAgICAgICAgICAgQiA9IDIwIAopCgpwbG90KHJmX3NoYXBfMikKYGBgCgogIAoqKkRvZXMgaXQgdGVsbCBhIHNpbWlsYXIgc3RvcnkgdG8gdGhlIGJyZWFrLWRvd24gcGxvdD8qKgoKV2UgY2FuIHNlZSB0aGF0IFNFVkVSQUwgdmFyaWFibGVzIGhhdmUgYm94cGxvdHMgb2YgdGhlaXIgZWZmZWN0cyBzdHJhZGRsaW5nIHplcm8gc3ByZWFkaW5nIGFjcm9zcyBib3RoIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSB2YWx1ZXMuICAKCgpUaGVzZSB2YXJpYWJsZXMgc2V0IHRvIHRoZWlyIGFwcHJvcHJpYXRlIGNvbnN0YW50cyBpbmNsdWRlOgoKdG90YWxfYmFsX2lsCgppbnRfcmF0ZQoKc3ViX2dyYWRlCgpudW1faWxfdGwKCmVtcF9sZW5ndGgKCnRvdGFsX2lsX2hpZ2hfY3JlZGl0X2xpbWl0CgoKVGhpcyBtZWFucyB0aGF0IHdoZW4gd2UgY2hhbmdlZCB0aGUgb3JkZXIgb2YgY29uc2lkZXJpbmcgdGhlc2UgdmFyaWFibGVzIGFuZCByZS1ydW4gdGhlIGJyZWFrZG93biB0ZXN0IDIwIHRpbWVzLCB3ZSBoYXZlIHRoZSB2YXJpYWJsZXMgaGF2aW5nIG11bHRpcGxlIGluc3RhbmNlcyBvZiBib3RoIG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSBlZmZlY3RzLiAgCgpUaGlzIG5hcnJhdGl2ZSBpcyBpbiBkaXJlY3QgY29uZmxpY3Qgd2l0aCB0aGUgY2xlYXIgY3V0IHBpY3R1cmUgd2Ugd2VyZSBwYWludGVkIGFib3ZlIGFib3V0IHZhcmlhYmxlIGVmZmVjdHMuICAKCiAgCiAgLSBDb25zdHJ1Y3QgYSBMSU1FIGdyYXBoIChmb2xsb3cgbXkgY29kZSBjYXJlZnVsbHkpLiAKICAKICAKYGBge3J9Cm1vZGVsX3R5cGUuZGFsZXhfZXhwbGFpbmVyIDwtIERBTEVYdHJhOjptb2RlbF90eXBlLmRhbGV4X2V4cGxhaW5lcgpwcmVkaWN0X21vZGVsLmRhbGV4X2V4cGxhaW5lciA8LSBEQUxFWHRyYTo6cHJlZGljdF9tb2RlbC5kYWxleF9leHBsYWluZXIKCnNldC5zZWVkKDIpCmxpbWVfcmZfMiA8LSBwcmVkaWN0X3N1cnJvZ2F0ZShleHBsYWluZXIgPSByZl9leHBsYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG9iczJfcTUgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLUNsYXNzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9mZWF0dXJlcyA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl9wZXJtdXRhdGlvbnMgPSAxMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAibGltZSIpCgpsaW1lX3JmXzIgJT4lIAogIHNlbGVjdChtb2RlbF9yMiwgbW9kZWxfcHJlZGljdGlvbiwgcHJlZGljdGlvbikgJT4lIAogIGRpc3RpbmN0KCkKYGBgCgogIApgYGB7cn0KcGxvdChsaW1lX3JmXzIpICsKICBsYWJzKHggPSAiVmFyaWFibGUiKQpgYGAKCiAgCiAgCioqSG93IGNsb3NlIGlzIGVhY2ggb3JpZ2luYWwgcHJlZGljdGlvbiB0byB0aGUgcHJlZGljdGlvbiBmcm9tIHRoZSBsb2NhbCBtb2RlbD8gSW50ZXJwcmV0IHRoZSByZXN1bHQuIFlvdSBjYW4gYWxzbyB0cnkgdXNpbmcgZmV3ZXIgb3IgbW9yZSB2YXJpYWJsZXMgaW4gdGhlIGxvY2FsIG1vZGVsIHRoYW4gSSB1c2VkIGluIHRoZSBleGFtcGxlLioqCiAgCiAgClByZWRpY3Rpb24gZnJvbSBPcmlnaW5hbCBSRiBNb2RlbDogMC43NAoKUHJlZGljdGlvbiBmcm9tIExvY2FsIE1vZGVsOiAgMC44MiAoKzAuMDgpCgoqKlRoZSBwcmVkaWN0aW9ucyBhcmUgcHJldHR5IGNsb3NlLi4udGhlIExvY2FsIE1vZGVsIGlzICswLjA4Li4uKioKCi0tIFRoZXkgYXJlIGJhc2ljYWxseSB0aGUgc2FtZSBiZWNhdXNlIG1vc3QgdmFyaWFibGVzIGhhdmUgcm91Z2hseSB0aGUgc2FtZSBpbXBhY3QuLi4gcHJvYmFibHkgYmVjYXVzZSB0aGUgdG90YWxfaWxfaGlnaF9jcmVkaXRfbGltaXQgdmFyaWFibGUgYXBwZWFycyB0byBoYXZlIHNsaWdodGx5IGdyZWF0ZXIgaW1wYWN0IGhlcmUgYW5kIHNvbWUgdmFyaWFibGVzIGltcGFjdHMgbWF5IGJlIG9uIHRoZSBoaWdoZXIgZW5kIG9mIHRoZWlyIGJveHBsb3RzLi4uYWxzbyBqdXN0IHNvIGhhcHBlbnMgMyAvIDUgdmFycyBoYXZlIHBvc2l0aXZlIGltcGFjdHMgb2YgYWxsIHNhbWUgc2l6ZS4uLi4gCgoKTG9jYWwgTW9kZWwgUl4yOiAwLjA1IC0tIE5vdCBHb29kCiAgCkJhciBQbG90IFNob3dzIFZhcmlhYmxlIEltcG9ydGFuY2UgRnJvbSBMb2NhbCBNb2RlbDogIAoKLS0gYW5udWFsIGluY29tZSBuZCB0b3RhbCBiYWwgaWwgaGF2ZSBiaWcgbmVnYXRpdmUgZWZmZWN0cwoKLS0gdGhlIHJlc3QgYXBwZWFyIHRvIGhhdmUgZXF1YWxseSBhcyBoaWdoIGltcGFjdCB2YXJzIGJ1dCBwb3NpdGl2ZQogIAogIAogIAoqKk9CU0VSVkFUSU9OIDIwKioKCmBgYHtyfQpvYnMyMCA8LSBsZW5kaW5nX3Rlc3QgJT4lIAogIHNsaWNlKDIwKQpvYnMyMApgYGAKCi0gQ29uc3RydWN0IGEgYnJlYWstZG93biBwbG90IHVzaW5nIHRoZSBkZWZhdWx0IG9yZGVyaW5nLiAKICAKYGBge3J9CnBwX3JmXzIwIDwtIHByZWRpY3RfcGFydHMoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImJyZWFrX2Rvd24iKSAKCnBsb3QocHBfcmZfMjApCmBgYAoKCioqSW50ZXJwcmV0IHRoZSByZXN1bHRpbmcgZ3JhcGguIFdoaWNoIHZhcmlhYmxlcyBjb250cmlidXRlIG1vc3QgdG8gZWFjaCBvYnNlcnZhdGlvbidzIHByZWRpY3Rpb24/KioKCgpGaXJzdCB3ZSBjYW4gc2VlICB0aGUgaW5pdGlhbCBiYXJzIHN0YXJ0IGF0IHRoZSBpbnRlcmNlcHQgb2YgMC44NDggd2hpY2ggbWFya3MgdGhlIHZhbHVlIG9mIHRoZSBhdmVyYWdlIHByZWRpY3RlZCBjbGFzcyAoMSA9IGdvb2QsIDAgPSBiYWQpIHdoZW4gYXBwbHlpbmcgdGhlIHJmIG1vZGVsIHRvIHRoZSB0cmFpbmluZyBkYXRhLiAgCgpXZSBjYW4gc2VlIHdlIGVuZCB1cCAwLjAxOCBsb3dlciBpbiBvdXIgcHJlZGljdGVkIGNsYXNzIG9mIHBheWluZyBiYWNrIGEgbG9hbiB0aGFuIHRoZSBpbnRlcmNlcHQuICBUaGlzIHZlcnkgbGl0dGxlIGNoYW5nZSBpcyBkdWUgdG8gdGhlIGZhY3QgdGhhdCBhIGxvdCBvZiB0aGUgdmFyaWFibGVzJyBlZmZlY3RzIGhhdmUgY2FuY2VsbGluZyBlZmZlY3RzIChmYWlybHkgZXZlbiBzcGxpdCBvZiArIGFuZCAtIGVmZmVjdHMpLiAgVGhlIHJlc3VsdGluZyAwLjgzIGlzIGEgc3Ryb25nIGdvb2QgcHJlZGljdGlvbi4gICAgIAoKV2UgY2FuIHNlZSB0aGF0IHRoZSBsYXJnZXN0IHR1Z3MgY29tZSBmcm9tIHZhcmlhYmxlcyBib3RoICsgYW5kIC0gKGFuZCBJIGd1ZXNzIHRoYXQgbWFrZXMgdGhlbSBtb3N0IGluZmx1ZW50aWFsIGFzIHRoZXkgY2FuY2VsIGVhY2ggb3RoZXIgb3V0IGZvciB0aGUgbW9zdCBwYXJ0KS4uLjoKCgppbnRfcmF0ZSA9PSAxMi45OSAtLT4gLTAuMDQgCgp0b3RhbF9iYWxfaWwgPT0gMjYyNzUgLS0+IDAuMDM3CgppbnFfZmkgPT0gNCAtLT4gLTAuMDI4CgoKVGhpcyBtZWFucyB0aGF0IGZvciBlYWNoIG9mIHRoZXNlIHZhcmlhYmxlcywgaWYgdGhleSB3ZXJlIGZpeGVkIGF0IHRoZSB2YWx1ZXMgdGhleSBhcmUgc2V0IHRvLCB0aGUgY2hhbmdlIGluIGF2ZXJhZ2UgY2xhc3MgcHJlZGljdGlvbiB3b3VsZCBiZSB0aGVpciBvdXRwdXR0ZWQgdmFscy4gIAoKVGhpcyB3YXMgYSB3ZWlyZCBvbmUgdG8gZXZhbCBjb250cmlidXRpb246IDEpIGRvIHRoZSBiaWdnZXIgdHVncyBzdGlsbCBjb3VudCBhcyBtb3N0IHNpZ25pZmljYW50IGJlY2F1c2UgdGhleSBhcmUgY2FuY2VsbGluZyBlYWNob3RoZXIgb3V0IG9yIDIpIGRvIHRoZSBzbWFsbCBuZWdhdGl2ZSB0dWdzIGNvdW50IG1vcmUgZXZlbiB0aG91Z2ggdGhleSB3b3VsZG4ndCBtYXR0ZXIgaWYgdGhlIGJpZyB0dWdzIGRpZG4ndCBjYW5jZWwgb3V0Pz8/PwoKICAKICAtIENvbnN0cnVjdCBhIFNIQVAgZ3JhcGggYW5kIGludGVycHJldCBpdC4gCiAgCmBgYHtyfQpyZl9zaGFwXzIwIDwtcHJlZGljdF9wYXJ0cyhleHBsYWluZXIgPSByZl9leHBsYWluLAogICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyMCwKICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzaGFwIiwKICAgICAgICAgICAgICAgICAgICAgICAgQiA9IDIwIAopCgpwbG90KHJmX3NoYXBfMjApCmBgYAogIAogIAogIAogICoqRG9lcyBpdCB0ZWxsIGEgc2ltaWxhciBzdG9yeSB0byB0aGUgYnJlYWstZG93biBwbG90PyoqCgpXZSBjYW4gc2VlIHRoYXQgU0VWRVJBTCB2YXJpYWJsZXMgaGF2ZSBib3hwbG90cyBvZiB0aGVpciBlZmZlY3RzIHN0cmFkZGxpbmcgemVybyBzcHJlYWRpbmcgYWNyb3NzIGJvdGggcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHZhbHVlcy4gIAoKClRoZXNlIHZhcmlhYmxlcyBzZXQgdG8gdGhlaXIgYXBwcm9wcmlhdGUgY29uc3RhbnRzIGluY2x1ZGU6Cgp0b3RhbF9iYWxfaWwKCnN1Yl9ncmFkZQoKYW5udWFsX2luYwoKaW50X3JhdGUKCmFkZHJfc3RhdGUKCmFsbF91dGlsCgppbnFfbGFzdF82bXRocwoKClRoaXMgbWVhbnMgdGhhdCB3aGVuIHdlIGNoYW5nZWQgdGhlIG9yZGVyIG9mIGNvbnNpZGVyaW5nIHRoZXNlIHZhcmlhYmxlcyBhbmQgcmUtcnVuIHRoZSBicmVha2Rvd24gdGVzdCAyMCB0aW1lcywgd2UgaGF2ZSBhbG1vc3QgZXZlcnkgc2luZ2xlIHZhcmlhYmxlIGhhdmluZyBtdWx0aXBsZSBpbnN0YW5jZXMgb2YgYm90aCBuZWdhdGl2ZSBhbmQgcG9zaXRpdmUgZWZmZWN0cy4gIAoKVGhpcyBuYXJyYXRpdmUgaXMgaW4gZGlyZWN0IGNvbmZsaWN0IHdpdGggdGhlIGNsZWFyIGN1dCBwaWN0dXJlIHdlIHdlcmUgcGFpbnRlZCBhYm92ZSBhcyB0aGUgdmFycyBlZmZlY3RzIGFyZSBub3QgYXMgY2xlYXIgYXMgd2UgdGhvdWdodCBhYm92ZS4gIAoKICAKICAtIENvbnN0cnVjdCBhIExJTUUgZ3JhcGggKGZvbGxvdyBteSBjb2RlIGNhcmVmdWxseSkuIAogIAoKYGBge3J9Cm1vZGVsX3R5cGUuZGFsZXhfZXhwbGFpbmVyIDwtIERBTEVYdHJhOjptb2RlbF90eXBlLmRhbGV4X2V4cGxhaW5lcgpwcmVkaWN0X21vZGVsLmRhbGV4X2V4cGxhaW5lciA8LSBEQUxFWHRyYTo6cHJlZGljdF9tb2RlbC5kYWxleF9leHBsYWluZXIKCnNldC5zZWVkKDIpCmxpbWVfcmZfMjAgPC0gcHJlZGljdF9zdXJyb2dhdGUoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyMCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtQ2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX2ZlYXR1cmVzID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3Blcm11dGF0aW9ucyA9IDEwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJsaW1lIikKCmxpbWVfcmZfMjAgJT4lIAogIHNlbGVjdChtb2RlbF9yMiwgbW9kZWxfcHJlZGljdGlvbiwgcHJlZGljdGlvbikgJT4lIAogIGRpc3RpbmN0KCkKYGBgCgogIApgYGB7cn0KcGxvdChsaW1lX3JmXzIwKSArCiAgbGFicyh4ID0gIlZhcmlhYmxlIikKYGBgCiAgCiAgCiAgCioqSG93IGNsb3NlIGlzIGVhY2ggb3JpZ2luYWwgcHJlZGljdGlvbiB0byB0aGUgcHJlZGljdGlvbiBmcm9tIHRoZSBsb2NhbCBtb2RlbD8gSW50ZXJwcmV0IHRoZSByZXN1bHQuIFlvdSBjYW4gYWxzbyB0cnkgdXNpbmcgZmV3ZXIgb3IgbW9yZSB2YXJpYWJsZXMgaW4gdGhlIGxvY2FsIG1vZGVsIHRoYW4gSSB1c2VkIGluIHRoZSBleGFtcGxlLioqCiAgCiAgClByZWRpY3Rpb24gZnJvbSBPcmlnaW5hbCBSRiBNb2RlbDogMC44MwoKUHJlZGljdGlvbiBmcm9tIExvY2FsIE1vZGVsOiAgMC44ICgtMC4wMykKCioqVGhlIHByZWRpY3Rpb25zIGFyZSBmYWlybHkgY2xvc2UuLi50aGUgTG9jYWwgTW9kZWwgaXMgLTAuMDMuLi4qKgoKLS0gVGhpcyBsaWtlbHkgaXMgYmVjYXVzZSBvZiB0aGUgbWFzc2l2ZSBuZWdhdGl2ZSBpbXBhY3Qgb2YgcmV2b2xfdXRpbCBhbmQgYW5udWFsX2luYyB3aGljaCBwdXNoZWQgdGhlIHByZWQgc2xpZ2h0bHkgYmVsb3cgYXMgdGhleSBjb3VudGVyZWQgdGhlIHBvcyBpbXBhY3QgZnJvbSBvdGhlcnMuICBBbm51YWwgSW5jb21lJ3MgZWZmZWN0cyBhcHBlYXIgdG8gYmUgbXVjaCBtb3JlIG5lZ2F0aXZlIGhlcmUgdGhhbiBhYm92ZS4uLiAgCgpMb2NhbCBNb2RlbCBSXjI6IDAuMDcgLS0gVGVycmlibGUKICAKQmFyIFBsb3QgU2hvd3MgVmFyaWFibGUgSW1wb3J0YW5jZSBGcm9tIExvY2FsIE1vZGVsOiAgCgotLSByZXZvbF91dGlsIGhhcyBhIG1hc3NpdmUgbmVnYXRpdmUgaW1wYWN0CgotLSBhbm51YWwgaW5jb21lIGhhcyBlcXVhbGx5IG1hc3NpdmUgbmVnYXRpdmUgaW1wYWN0CgotLSBpbnEgbGFzdCA2IG10aHMgYW5kIHRvdGFsIGlsIGhpZ2ggY3JlZGl0IGhhdmUgbW9kZXJhdGVseSBsYXJnZSBwb3NpdGl2ZSBpbXBhY3RzCiAgCgoKKipPQlNFUlZBVElPTiAyMDAqKgoKYGBge3J9Cm9iczIwMCA8LSBsZW5kaW5nX3Rlc3QgJT4lIAogIHNsaWNlKDIwMCkKb2JzMjAwCmBgYAoKLSBDb25zdHJ1Y3QgYSBicmVhay1kb3duIHBsb3QgdXNpbmcgdGhlIGRlZmF1bHQgb3JkZXJpbmcuIAogIApgYGB7cn0KcHBfcmZfMjAwIDwtIHByZWRpY3RfcGFydHMoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdfb2JzZXJ2YXRpb24gPSBvYnMyMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJicmVha19kb3duIikgCgpwbG90KHBwX3JmXzIwMCkKYGBgCiAgCiAgCioqSW50ZXJwcmV0IHRoZSByZXN1bHRpbmcgZ3JhcGguIFdoaWNoIHZhcmlhYmxlcyBjb250cmlidXRlIG1vc3QgdG8gZWFjaCBvYnNlcnZhdGlvbidzIHByZWRpY3Rpb24/KioKCgpGaXJzdCB3ZSBjYW4gc2VlICB0aGUgaW5pdGlhbCBiYXJzIHN0YXJ0IGF0IHRoZSBpbnRlcmNlcHQgb2YgMC44NDggd2hpY2ggbWFya3MgdGhlIHZhbHVlIG9mIHRoZSBhdmVyYWdlIHByZWRpY3RlZCBjbGFzcyAoMSA9IGdvb2QsIDAgPSBiYWQpIHdoZW4gYXBwbHlpbmcgdGhlIHJmIG1vZGVsIHRvIHRoZSB0cmFpbmluZyBkYXRhLiAgICAKCldlIGNhbiBzZWUgd2UgZW5kIHVwIDAuMTMyIEhJR0hFUiBpbiBvdXIgcHJlZGljdGVkIGNsYXNzIG9mIHBheWluZyBiYWNrIGEgbG9hbiB0aGFuIHRoZSBpbnRlcmNlcHQgdGhhbmtzIHRvIHNvbWUgYmlnIHBvc2l0aXZlIGp1bXBzIGZyb20gaW5mbHVlbnRpYWwgdmFyaWFibGVzLiAgVGhlIHJlc3VsdGluZyAwLjk4IGlzIGFuIGV4dHJlbWVseSBzdHJvbmcgZ29vZCBwcmVkaWN0aW9uLiAgCgpXZSBjYW4gc2VlIHRoYXQgdGhlIGxhcmdlc3QgcG9zaXRpdmUganVtcHMgZnJvbSB2YXJpYWJsZXMgY29tZSBmcm9tOgoKCmludF9yYXRlID09IDEzLjQ0IC0tPiAwLjAyOSAKCnN1Yl9ncmFkZSA9PSAxMyAtLT4gMC4wMjkKCnRvdGFsX2JhbF9pbCA9PSAwIC0tPiAwLjAyOAoKYW5kIGFsc28gaXQgaGVscHMgdGhhdCBhbGwgYnV0IDMgaW4gdGhpcyBjYXNlIGhhdmUgcG9zaXRpdmUgaW1wYWN0cy4uLgoKClRoaXMgbWVhbnMgdGhhdCBmb3IgZWFjaCBvZiB0aGVzZSB2YXJpYWJsZXMsIGlmIHRoZXkgd2VyZSBmaXhlZCBhdCB0aGUgdmFsdWVzIHRoZXkgYXJlIHNldCB0bywgdGhlIGNoYW5nZSBpbiBhdmVyYWdlIGNsYXNzIHByZWRpY3Rpb24gd291bGQgYmUgdGhlc2UgbGFyZ2UgcG9zaXRpdmUgdmFsdWVzIHdoaWNoIHB1bGxlZCBvdXIgcHJlZGljdGlvbiBmb3IgdGhpcyBvYnNlcnZhdGlvbiB1cCB2ZXJ5IGhpZ2guICAKCiAgCiAgLSBDb25zdHJ1Y3QgYSBTSEFQIGdyYXBoIGFuZCBpbnRlcnByZXQgaXQuIAogIApgYGB7cn0KcmZfc2hhcF8yMDAgPC1wcmVkaWN0X3BhcnRzKGV4cGxhaW5lciA9IHJmX2V4cGxhaW4sCiAgICAgICAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG9iczIwMCwKICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzaGFwIiwKICAgICAgICAgICAgICAgICAgICAgICAgQiA9IDIwIAopCgpwbG90KHJmX3NoYXBfMjAwKQpgYGAKICAKICAKICAKKipEb2VzIGl0IHRlbGwgYSBzaW1pbGFyIHN0b3J5IHRvIHRoZSBicmVhay1kb3duIHBsb3Q/KioKCgpXZSBjYW4gc2VlIHRoYXQgU0VWRVJBTCB2YXJpYWJsZXMgaGF2ZSBib3hwbG90cyBvZiB0aGVpciBlZmZlY3RzIHN0cmFkZGxpbmcgemVybyBzcHJlYWRpbmcgYWNyb3NzIGJvdGggcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHZhbHVlcy4gIAoKClRoZXNlIHZhcmlhYmxlcyBzZXQgdG8gdGhlaXIgYXBwcm9wcmlhdGUgY29uc3RhbnRzIGluY2x1ZGU6Cgp0b3RhbF9iYWxfaWwKCnN1Yl9ncmFkZQoKaW50X3JhdGUKCnRvdGFsX2lsX2hpZ2hfY3JlZGl0X2xpbWl0Cgphbm51YWxfaW5jCgpudW1faWxfdGlsCgoKVGhpcyBtZWFucyB0aGF0IHdoZW4gd2UgY2hhbmdlZCB0aGUgb3JkZXIgb2YgY29uc2lkZXJpbmcgdGhlc2UgdmFyaWFibGVzIGFuZCByZS1ydW4gdGhlIGJyZWFrZG93biB0ZXN0IDIwIHRpbWVzLCB3ZSBoYXZlIHRoZXNlIHZhcmlhYmxlcyBoYXZpbmcgbXVsdGlwbGUgaW5zdGFuY2VzIG9mIGJvdGggbmVnYXRpdmUgYW5kIHBvc2l0aXZlIGVmZmVjdHMuICAKClRoaXMgbmFycmF0aXZlIGlzIGluIGRpcmVjdCBjb25mbGljdCB3aXRoIHRoZSBjbGVhciBjdXQgcGljdHVyZSB3ZSB3ZXJlIHBhaW50ZWQgYWJvdmUgYXMgd2Ugc2VlIHRoZSBpbXBhY3RzIG9mIHZhcnMgYXJlIG5vdCBhcyBvYnZpb3VzLiAgCiAgCiAgCiAgCiAgLSBDb25zdHJ1Y3QgYSBMSU1FIGdyYXBoIChmb2xsb3cgbXkgY29kZSBjYXJlZnVsbHkpLiAKICAKCmBgYHtyfQptb2RlbF90eXBlLmRhbGV4X2V4cGxhaW5lciA8LSBEQUxFWHRyYTo6bW9kZWxfdHlwZS5kYWxleF9leHBsYWluZXIKcHJlZGljdF9tb2RlbC5kYWxleF9leHBsYWluZXIgPC0gREFMRVh0cmE6OnByZWRpY3RfbW9kZWwuZGFsZXhfZXhwbGFpbmVyCgpzZXQuc2VlZCgyKQpsaW1lX3JmXzIwMCA8LSBwcmVkaWN0X3N1cnJvZ2F0ZShleHBsYWluZXIgPSByZl9leHBsYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG9iczIwMCAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdCgtQ2xhc3MpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX2ZlYXR1cmVzID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3Blcm11dGF0aW9ucyA9IDEwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJsaW1lIikKCmxpbWVfcmZfMjAwICU+JSAKICBzZWxlY3QobW9kZWxfcjIsIG1vZGVsX3ByZWRpY3Rpb24sIHByZWRpY3Rpb24pICU+JSAKICBkaXN0aW5jdCgpCmBgYAoKICAKYGBge3J9CnBsb3QobGltZV9yZl8yMDApICsKICBsYWJzKHggPSAiVmFyaWFibGUiKQpgYGAKICAKICAKICAKICAqKkhvdyBjbG9zZSBpcyBlYWNoIG9yaWdpbmFsIHByZWRpY3Rpb24gdG8gdGhlIHByZWRpY3Rpb24gZnJvbSB0aGUgbG9jYWwgbW9kZWw/IEludGVycHJldCB0aGUgcmVzdWx0LiBZb3UgY2FuIGFsc28gdHJ5IHVzaW5nIGZld2VyIG9yIG1vcmUgdmFyaWFibGVzIGluIHRoZSBsb2NhbCBtb2RlbCB0aGFuIEkgdXNlZCBpbiB0aGUgZXhhbXBsZS4qKgogIAoKUHJlZGljdGlvbiBmcm9tIE9yaWdpbmFsIFJGIE1vZGVsOiAwLjk4CgpQcmVkaWN0aW9uIGZyb20gTG9jYWwgTW9kZWw6ICAwLjgxICgtMC4xNykKCioqVGhlIHByZWRpY3Rpb25zIGFyZSBub3QgY2xvc2UuLi50aGUgTG9jYWwgTW9kZWwgaXMgLTAuMTcuLi4qKgoKLS0gVGhpcyBsaWtlbHkgaXMgYmVjYXVzZSBoZXJlIHdlIGhhdmUgbWFzc2l2ZSBuZWdhdGl2ZSBpbXBhY3RzIHNlZW4gZnJvbSByZXZvbF91dGlsIGFuZCBhbm51YWxfaW5jb21lLCBtdWNoIGhpZ2hlciB0aGFuIGRpc3BsYXllZCBhYm92ZSwgd2hpY2ggYWNjb3VudCBmb3IgdGhlIG1hc3NpdmUgZHJvcCBpbiBwcmVkaWN0ZWQgY2xhc3Mgc2NvcmUuICAKCgpMb2NhbCBNb2RlbCBSXjI6IDAuMDYgLS0gQXdmdWwKICAKQmFyIFBsb3QgU2hvd3MgVmFyaWFibGUgSW1wb3J0YW5jZSBGcm9tIExvY2FsIE1vZGVsOiAgCgotLSByZXZvbF91dGlsLCBhbm51YWwgaW5jb21lIGhhdmUgbWFzc2l2ZWx5IGxhcmdlIG5lZ2F0aXZlIGltcGFjdHMgdGhhdCBvdXRwYWNlIHRoZSBwb3NpdGl2ZSBpbXBhY3RzIG9mIGlucSBsYXN0IDZtdGhzLCBpbnFfZmksIGFuZCB0b3RhbF9iYWxfaWwuICAKICAKICAKKio2LiBEZXNjcmliZSBob3cgeW91IHdvdWxkIHVzZSB0aGUgaW50ZXJwcmV0YWJsZSBtYWNoaW5lIGxlYXJuaW5nIHRvb2xzIHdlJ3ZlIGxlYXJuZWQgKGJvdGggbG9jYWwgYW5kIGdsb2JhbCkgaW4gZnV0dXJlIG1hY2hpbmUgbGVhcm5pbmcgcHJvamVjdHM/IEhvdyBkb2VzIGVhY2ggb2YgdGhlbSBoZWxwIHlvdT8qKgoKCkkgY291bGQgZmluZCBncmVhdCB1c2UgaW4gdXNpbmcgdGhlIGJveHBsb3QgYW5kIGhpc3RvZ3JhbSBvZiByZXNpZHVhbHMgZm9yIGEgbW9kZWwgZnJvbSBnbG9iYWwgaW50ZXJwcmV0YWJsZSBNTCBhbmQgZnJvbSBsb2NhbCBpbnRlcnByZXRhYmxlIE1MIEkgd291bGQgZGVmaW5pdGVseSBoYXZlIGdyZWF0IHZhbHVlIGZyb20gdXNpbmcgYnJlYWtkb3duIHByb2ZpbGVzIGFuZCBzaGFwbGV5IHBsb3RzLiAKCioqRm9yIEdsb2JhbDogIFZpc3VhbGl6aW5nIFJlc2lkdWFscyoqIAoKVGhpcyBwYXN0IFN1bW1lciwgSSBoYWQgYW4gaW50ZXJuc2hpcCB3aXRoIGEgRmludGVjaCBjb21wYW55IGNhbGxlZCBBdmlkWENoYW5nZSwgYSBDaGFybG90dGUsIE5DIGJhc2VkIEFjY291bnRzIFBheWFibGUgQXV0b21hdGlvbiBjb21wYW55LiAgSSB1c2VkIHNxbCB0byBwdWxsIGRhdGEgZnJvbSBkYXRhYmFzZXMgYW5kIG1hY2hpbmUgbGVhcm5pbmcgc2tpbGxzIHRvIHVzZSBuZXVyYWwgbmV0d29ya3MgYW5kIGRlZXAgbGVhcm5pbmcuICBXZSB3YW50ZWQgdG8gbW9yZSBhY2N1cmF0ZWx5IHByZWRpY3RlZCB3aGljaCBvZiBvdXIgY2xpZW50cyBhY2NlcHRlZCBWaXJ0dWFsIENyZWRpdCBDYXJkIGFuZCB3aGljaCBkaWRuJ3QgYXMgdGhpcyB3YXMgYSBiaWcgcHJvYmxlbS4gIEF2aWQgbWFrZXMgbW9zdCBvZiB0aGVpciBtb25leSBmcm9tIGZlZXMgb24gVkNDIGNhcmRzIGJlaW5nIHVzZWQgdG8gcGF5IG9mZiBpbnZvaWNlcyBhcyBvcHBvc2VkIHRvIGp1c3QgZGlyZWN0IGRlcG9zaXQgKHRoaXMgaXMgbW9yZSBpZGVhbCBmb3IgbW9zdCBhcyB0aGV5IGdldCB0aGUgbW9uZXkgbXVjaCBxdWlja2VyIGRlc3BpdGUgYSB2ZXJ5IHNtYWxsIGZlZSkuICBJZiBhYmxlIHRvIGJldHRlciBpZGVudGlmeSB3aG8gc2ltcGx5IGNhbid0IHRha2UgVkNDLCBpdCB3b3VsZCBzYXZlIGEgbG90IG9mIHRpbWUgYW5kIG1vbmV5IGJldHRlciB1c2VkIG9uIG90aGVyIGNsaWVudHMuCgpXaGVuIHdlIGZpbmFsaXplZCB0aGUgbW9kZSwgSSB3b3VsZCBoYXZlIGxvdmVkIHRvIHNlZSBob3cgd2VsbCBpdCBwcmVkaWN0ZWQgdGhyb3VnaCBzZWVpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBpdHMgcmVzaWR1YWxzLiAgTXkgbW9kZWwgYWxzbyB3YXMgYSBjbGFzcyBwcmVkaWN0aW9uIGxpa2UgdyB0aGUgbGVuZGluZyBkYXRhLCBzcGVjaWZpY2FsbHkgdXNpbmcgdGhlIHNwZWxsaW5nIGFuZCBtYWtlIHVwIG9mIHRoZSBjb21wYW55IG5hbWUgdG8gcHJlZGljdCBpZiB0aGV5IHRvb2sgVkNDICh0aGUgaHVuY2ggd2FzIHRoYXQgY29tcGFuaWVzIHRoYXQgYXBwZWFyZWQgdG8gYmUgcGVvcGxlIGRpZCBub3QgYWNjZXB0IFZDQyBhbmQgdGhlIG1vZGVsIHVzZWQgYSBzZXJpZXMgb2YgZGljdGlvbmFyaWVzIGZvciBsZXR0ZXJzIGFuZCBuYW1lcyBmcm9tIGNlbnN1cyBkYXRhIHRvIGRpc3Rpbmd1aXNoIG5hbWVzKS4gIAoKSWYgSSBjb3VsZCBhbmFseXplIHRoZSByZXNpZHVhbHMgYSBiaXQgbW9yZSB3aXRoIHRoaW5ncyBsaWtlIHRoZSBib3hwbG90IGFuZCBoaXN0b2dyYW0gbGlrZSB3ZSBkaWQgYWJvdmUsIEkgd291bGQgaGF2ZSBiZWVuIGFibGUgdG8gc2VlIGEgYml0IGJldHRlciBob3cgd2VsbCB3ZSBwcmVkaWN0ZWQgY29ycmVjdGx5LiAgU3BlY2lhbGx5LCBJIHdvdWxkIGhhdmUgYmVlbiBhYmxlIHRvIHNlZSBpZiBpdCB3YXMgYSBnb29kIG1vZGVsIGJleW9uZCBzaW1wbHkgbG9va2luZyBhdCBvdXRwdXQgbWV0cmljcyBsaWtlIEkgZGlkLiAgSWYgSSBjb3VsZCBoYXZlIHZpc3VhbGl6ZWQgdGhlIHJlc2lkdWFscyBhbmQgc2VlbiBob3cgaXQgdGVuZGVkIHRvIHByZWRpY3QgdGhlIG1ham9yaXR5IGNsYXNzIG1vcmUgdGhhbiBtb3N0IG90aGVyIHRoaW5ncywgdGhhdCB3b3VsZCBoYXZlIGJlZW4gdmVyeSBlYXN5IHRvIHNlZSBhbmQgY29vbCB0byBoZWxwIHNob3cgdGhlIHJlc3VsdHMgYXMgb3Bwb3NlZCB0byBzaW1wbHkgdGVsbGluZyBwZW9wbGUgdGhlIHJlc3VsdHMgd2l0aCBtZXRyaWNzIGZpbGxlZCB3aXRoIGphcmdvbi4gICAKCioqRm9yIExvY2FsOiAgQnJlYWtkb3duIFByb2ZpbGVzIGFuZCBTaGFwbGV5KioKCkFsc28sIGluIG15IGludGVybnNoaXAgSSBhZGRlZCBvbiB0byBhIFZDQyBtb2RlbCBhIHByb3BlbnNpdHkgc2NvcmUgdmFsdWUgd2hpY2ggcHJlZGljdGVkIGEgcHJvYmFiaWxpdHkgYXQgd2hpY2ggYSBjb21wYW55ICh3ZSB0aG91Z2h0KSB3b3VsZCBhY2NlcHQgVkNDLiAgSXQgdG9vayBpbiBtYW55IHZhcmlhYmxlcyBpbmNsdWRpbmcgcGFzdCBpbnZvaWNlcywgc2l6ZSBvZiB0aGUgaW52b2ljZSwgc2l6ZSBvZiB0aGUgY29tcGFueSwgaWYgdGhleSBoYXZlIHRha2VuIFZDQyBpbiB0aGUgcGFzdCBiZWZvcmUsIGxvY2F0aW9uLi4uIAoKSSB3b3VsZCBoYXZlIGxvdmVkIHRvIHNlZSBmb3Igc3BlY2lmaWMgY29tcGFuaWVzIHdoaWNoIHZhcmlhYmxlcyB3ZXJlIG1vcmUgc2lnbmlmaWNhbnQgaW4gZHJpdmluZyB0aGUgcHJvcGVuc2l0eSBzY29yZXMgdG8gd2hlcmUgdGhleSB3ZW50LiAgT2J2aW91c2x5IFBhbmVyYSB0YWtlcyBWQ0MgYmVjYXVzZSB0aGV5IHRha2UgY3JlZGl0IGNhcmQgYnV0IHdoYXQgdmFyaWFibGUgc3BlY2lmaWNhbGx5IHdhcyBob25pbmcgaW4gb24gdGhpcz8KClNlZWluZyB0aGUgbGl0dGxlIHR1ZyBvZiB3YXIgYmV0d2VlbiB2YXJpYWJsZXMgYW5kIGhvdyB0aGV5IGVmZmVjdGVkIHRoZSBvdXRjb21lIHdvdWQgaGF2ZSBiZWVuIHJlYWxseSBjb29sLiAgQWRkaXRpb25hbGx5LCBJIHdvdWxkIGhhdmUgbGlrZWQgdG8gc2VlIHRoZSBwZXJtYW5lbmNlIG9mIHRoZXNlIHZhcmlhYmxlcyBlZmZlY3RzIGJleW9uZCBqdXN0IG9uZSBvYnNlcnZhdGlvbi4gSWYgSSBjb3VsZCBoYXZlIGFsc28gdXNlZCBhIHNoYXAgcGxvdCwgSSBjb3VsZCBoYXZlIHNlZW4gaG93IGZpcm0gdGhlc2UgdmFyaWFibGUgaW1wYWN0cyB3ZXJlIGFzIHdlbGwgYW5kIHNlZW4gaG93IGl0IGNoYW5nZWQgZnJvbSBsYXJnZSB0byBzbWFsbCBjb21wYW55IG9jY3VycmVuY2VzLiAgIAoKCioqNy4gU2F2ZSB0aGlzIGZpbmFsIG1vZGVsIHVzaW5nIHRoZSBgd3JpdGVfcmRzKClgIGZ1bmN0aW9uIC0gc2VlIHRoZSBzZWN0aW9uIG9mIHRoZSBgdGlkeW1vZGVsc2AgaW50cm8gZm9yIGEgc2ltaWxhciBleGFtcGxlLCBidXQgd2UncmUgdXNpbmcgYHdyaXRlX3JkcygpYCBpbnN0ZWFkIG9mIGBzYXZlUkRTKClgLiBXZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBtb2RlbCBpbiB0aGUgbmV4dCBwYXJ0LiBZb3UnbGwgd2FudCB0byBzYXZlIGl0IGluIHRoZSBmb2xkZXIgd2hlcmUgeW91IGNyZWF0ZSB5b3VyIHNoaW55IGFwcC4gUnVuIHRoZSBjb2RlLCBhbmQgdGhlbiBhZGQgYGV2YWw9RkFMU0VgIHRvIHRoZSBjb2RlIGNodW5rIG9wdGlvbnMgKG5leHQgdG8gdGhlIHIgaW5zaWRlIHRoZSBjdXJseSBicmFja2V0cykgc28gaXQgZG9lc24ndCByZXJ1biB0aGlzIGVhY2ggdGltZSB5b3Uga25pdC4qKiAKCmBgYHtyLCBldmFsPUZBTFNFfQojICMgZmluYWxpemUgbW9kZWwKIyByZl9maW5hbCA8LSByZl93b3JrZmxvdyAlPiUgCiMgICBmaW5hbGl6ZV93b3JrZmxvdyhiZXN0X2FjY3VyYWN5KSAlPiUgCiMgICBmaXQoZGF0YSA9IGxlbmRpbmdfdHJhaW5pbmcpCgoKd3JpdGVfcmRzKHJmX2ZpbmFsLCAicmZfZmluYWwucmRzIikKCnJmX2ZpbmFsX3JlYWQgPC0gcmVhZFJEUygicmZfZmluYWwucmRzIikKCgpgYGAKCgoKCiMjIFNoaW55IGFwcCAtLSBET05FCgpNeSBBcHA6IGh0dHBzOi8vYWpwMjgxMTcuc2hpbnlhcHBzLmlvL0xlbmRpbmdfQ2x1Yi8KClJlcG8gZm9yIEFwcDogaHR0cHM6Ly9naXRodWIuY29tL2FwYWxtYTEyNy9sZW5kaW5nX3NtYWxsCgpXZWJzaXRlIFBvc3Q6IGh0dHBzOi8vYW50aG9ueXBhbG1hLm5ldGxpZnkuYXBwL3Bvc3RzL3NoaW55YXBwcy8gKH5+IHNjcm9sbCBkb3duIHRvIGxlbmRpbmcgZGF0YSBzaGlueSBhcHAgfn4pCgoKIyMgRGF0YSBFdGhpY3M6IERhdGEgdmlzdWFsaXphdGlvbiBwcmluY2lwbGVzIC0tIERPTkUKCldlcmUgdGhlcmUgYW55IHByaW5jaXBsZXMgbWVudGlvbmVkIHRoYXQgeW91IGhhZG4ndCBoZWFyZCBvZiBiZWZvcmU/IAoKKipJIHdhcyB1bmF3YXJlIHRoYXQgdGhlIHkgYXhpcyBvbiBsaW5lIGdyYXBocyBzaG91bGRuJ3QgaW5jbHVkZSAwIGFuZCB0aGF0IHRoZSBncmFwaCBzaG91bGQgYmUgbXVjaCBtb3JlIGhvbmVkIGluIG9uIHRoZSByZWdpb24gb2YgaW50ZXJlc3QgdGhhbiB6b29tZWQgb3V0LiAgSSBhY3R1YWxseSBoYXZlIG5ldmVyIGRvbmUgdGhpcyB0byBiZSBob25lc3QganVzdCBiZWNhdXNlIEkgdGhvdWdodCBpdCB3YXMgZ29vZCBldGlxdWV0dGUgdG8gYWx3YXlzIHN0YXJ0IGF0IDAuLi4qKgoKV2hhdCBncmFwaCBzdG9vZCBvdXQgZm9yIHlvdSBhcyAidGhlIHdvcnN0Ij8gCgoqKlRoZSB3b3JzdCBoYXMgdG8gYmUgdGhlIGJ1YmJsZSBjaGFydCB3aXRoIHRoZSBwcm9mZXNzb3IgaW4gZnJvbnQuICBXaGlsZSBpdCBkb2VzIGhhdmUgYSBsb3Qgb2YgZGF0YSBhbmQgY29vbCB0aGluZ3MgZ29pbmcgb24sIGl0IGlzIGEgbWVzcy4gIFdpdGggbm8gYXhlcyBhbmQgcmFuZG9tIHNpemUgZGlmZmVyZW5jZXMsIHRoZXJlIGlzIHNvIG11Y2ggZ29pbmcgb24gdGhlIG9ubHkgdGhpbmcgeW91IGNhbiBzZWUgaXMgcmVsYXRpdmUgZGlmZmVyZW5jZS4gIEFkZGl0aW9uYWxseSwgdGhlcmUgaXMgYSBsb3Qgb2YgYmxvYmJpbmcgb2YgcG9pbnRzIG9uIHRvcCBvZiBlYWNoIG90aGVyIG1ha2luZyBldmVuIHJlbGF0aXZlIGRpZmZlcmVuY2UgZGlmZmljdWx0IHRvIHNlZS4qKgoKRGlkIGFueSBvZiB0aGUgZ3JhcGhzIGZvb2wgeW91PyAKCioqVGhlIGd1biBkZWF0aHMgaW4gRkwgZ3JhcGggd2FzIHZlcnl5eXl5IGNvbmZ1c2luZyBiZWNhdXNlIG9mIHRoZSB1cHNpZGUgZG93biBmaWxsaW5nIG9mIGl0LiAgQXQgZmlyc3QgSSB3YXMgbGlrZSB3b3cgc3VwZXIgd2VpcmQgc3RhbmQgeW91ciBncm91bmQgREVDUkVBU0VEIGd1biBkZWF0aHMgYnV0IHRoYXQgd2FzIHNpbXBseSBiZWNhdXNlIHRoZSBjcmVhdG9yIG9mIHRoZSBncmFwaCB3YW50ZWQgeW91IHRvIHNlZSBxdWl0ZSB0aGUgb3Bwb3NpdGUgb2YgcmVhbGl0eS4qKgoKSG93IGRvZXMgcHJhY3RpY2luZyBnb29kIGRhdGEgdmlzdWFsaXphdGlvbiBwcmluY2lwbGVzIGZpdCBpbiB3aXRoIGRhdGEgZXRoaWNzPwoKKipUaGUgZmxvcmlkYSBndW4gZGVhdGhzIG9uZSB3YXMgYSByZWFsbHkgZ29vZCBleGFtcGxlIG9mIGhvdyBiYWQgZGF0YSBwcmFjdGljZXMsIG5vIG1hdHRlciB0aGUgaW50ZW50LCBjYW4gYWN0dWFsbHkgbWFuaXB1bGF0ZSB0aGUgdHJ1dGggYW5kIGxpZSB0byB0aGUgdmlld2VyLiAgU3VjaCBmYWtlIG5ld3Mgd291bGQgYmUgZGFtYWdpbmcgdG8gYSBuYXJyYXRpdmUgaW4gYW4gZWxlY3Rpb24gZXNwZWNpYWxseSBiZWNhdXNlIG9mIGhvdyBjb250cmFyeSB0byByZWFsaXR5IGl0IGlzLiAgSWYgc29tZW9uZSB3ZXJlIHRvIGRvIHRoaXMgZHVyaW5nIGVsZWN0aW9uIHRpbWUgd2l0aCBpbXBvcnRhbnQgc3RhdHMgYWJvdXQgYWJvcnRpb24gYW5kIGltbWlncmF0aW9uLCBpdCB3b3VsZCBiZSBjcnVjaWFsIHRvIGNoYW5naW5nIG1pbmRzIGFuZCB2b3Rlcy4qKgo=